#include "../include/guafs.h"
/******************************************************************************
* SECTION: 宏定义
*******************************************************************************/
#define OPTION(t, p)        { t, offsetof(struct custom_options, p), 1 }

/******************************************************************************
* SECTION: 全局变量
*******************************************************************************/
static const struct fuse_opt option_spec[] = {		/* 用于FUSE文件系统解析参数 */
	OPTION("--device=%s", device),
	FUSE_OPT_END
};


/******************************************************************************
* SECTION: FUSE操作定义
*******************************************************************************/
static struct fuse_operations operations = {
	.init = guafs_init,						 /* mount文件系统 */		
	.destroy = guafs_destroy,				 /* umount文件系统 */
	.mkdir = guafs_mkdir,					 /* 建目录，mkdir */
	.getattr = guafs_getattr,				 /* 获取文件属性，类似stat，必须完成 */
	.readdir = guafs_readdir,				 /* 填充dentrys */
	.mknod = guafs_mknod,					 /* 创建文件，touch相关 */
	.write = NULL,								  	 /* 写入文件 */
	.read = NULL,								  	 /* 读文件 */
	.utimens = guafs_utimens,				 /* 修改时间，忽略，避免touch报错 */
	.truncate = NULL,						  		 /* 改变文件大小 */
	.unlink = NULL,							  		 /* 删除文件 */
	.rmdir	= NULL,							  		 /* 删除目录， rm -r */
	.rename = NULL,							  		 /* 重命名，mv */

	.open = NULL,							
	.opendir = NULL,
	.access = NULL
};
/******************************************************************************
* SECTION: 必做函数实现
*******************************************************************************/
/**
 * @brief 挂载（mount）文件系统
 * 
 * @param conn_info 可忽略，一些建立连接相关的信息 
 * @return void*
 */
void* guafs_init(struct fuse_conn_info * conn_info) {
	/* TODO: 在这里进行挂载 */
	struct guafs_super_d super_d;
	struct guafs_dentry* root_dentry;

	struct guafs_inode* root_inode;
	int super_blks;
	int inode_num;
	int map_inode_blks;
	int map_data_blks;
	int inode_blks;
	int is_init=0;

	super.fd = ddriver_open(guafs_options.device);
	
	if (super.fd<0)
	{
		perror("no enough room for driver");
		fuse_exit(fuse_get_context()->fuse);
	}
	ddriver_ioctl(super.fd,IOC_REQ_DEVICE_SIZE,&super.disk_sz);
	
	root_dentry=new_dentry("/",DIR);
	
	// 从磁盘位置中读入super块的数据
	if(guafs_driver_read(0,(uint8_t *)(&super_d),sizeof(struct guafs_super_d))!=0)
	{
		fuse_exit(fuse_get_context()->fuse);
	}

	// 若super块未被初始化过
	if(super_d.magic!=GUA_MAGIC_NUM)
	{
		super_blks=ROUND_UP(sizeof(struct guafs_super_d),BLOCK_SIZE)/BLOCK_SIZE;
		// 用每个文件的inode占用块和data占用块来估计磁盘可装入的文件数
		// 只是一个粗略估计
		inode_num=super.disk_sz/((INODE_BLOCK_NUM+DATA_BLOCK_NUM)*BLOCK_SIZE);
		map_inode_blks=ROUND_UP(ROUND_UP(inode_num,UINT32_BITS),BLOCK_SIZE)/BLOCK_SIZE;
		map_data_blks=ROUND_UP(ROUND_UP(inode_num*DATA_BLOCK_NUM,UINT32_BITS),BLOCK_SIZE)/BLOCK_SIZE;
		inode_blks=INODE_BLOCK_NUM*inode_num;

		super_d.map_inode_blks=map_inode_blks;
		super_d.map_data_blks=map_data_blks;
		super_d.max_ino=inode_num;
		super_d.map_inode_offset=BLKS_SZ(super_blks);
		super_d.map_data_offset=BLKS_SZ((super_blks+map_inode_blks));
		super_d.inode_offset=BLKS_SZ((super_blks+map_inode_blks+map_data_blks));
		super_d.data_offset=BLKS_SZ((super_blks+map_inode_blks+map_data_blks+inode_blks));
		super_d.usage_sz=0;
		super_d.cur_inode_num=0;
		is_init=1;
	}
	super.max_ino=super_d.max_ino;
	super.map_inode_offset=super_d.map_inode_offset;
	super.map_inode_blks=super_d.map_inode_blks;
	super.map_data_blks=super_d.map_data_blks;
	super.inode_map=(uint8_t*)malloc(BLKS_SZ(super.map_inode_blks));
	super.map_data_offset=super_d.map_data_offset;
	super.data_map=(uint8_t*)malloc(BLKS_SZ(super.map_data_blks));
	super.usage_sz=0;
	super.inode_offset=super_d.inode_offset;
	super.data_offset=super_d.data_offset;
	super.cur_inode_num=super_d.cur_inode_num;

	//将inode MAP读入内存
	if (guafs_driver_read(super.map_inode_offset, (uint8_t *)(super.inode_map), 
                        BLKS_SZ(super.map_inode_blks)) != 0) {
        fuse_exit(fuse_get_context()->fuse);
    }

	//将data MAP读入内存
	if (guafs_driver_read(super.map_data_offset, (uint8_t *)(super.data_map), 
							BLKS_SZ(super.map_data_blks)) != 0) {
		fuse_exit(fuse_get_context()->fuse);
	}

    if (is_init) {                                    /* 分配根节点 */
        root_inode = guafs_alloc_inode(root_dentry);
        guafs_sync_inode(root_inode);
    }
	else{
		root_inode = guafs_read_inode(root_dentry,GUAFS_ROOT_INO);
	}
	// 规定值
	root_inode->link=2;
	root_dentry->inode=root_inode;
	super.root_dentry = root_dentry;
	super.is_mount=1;
	guafs_dump_map();
	return NULL;
}

/**
 * @brief 卸载（umount）文件系统
 * 
 * @param p 可忽略
 * @return void
 */
void guafs_destroy(void* p) {
	struct guafs_super_d  super_d; 
	// 没有挂载直接返回
    if (!super.is_mount) {
        return ;
    }
// 写回inode和data
	guafs_sync_inode(super.root_dentry->inode);

	super_d.magic           	= GUAFS_MAGIC;
    super_d.usage_sz            = super.usage_sz;
	super_d.max_ino             = super.max_ino;

    super_d.map_inode_blks      = super.map_inode_blks;
    super_d.map_inode_offset    = super.map_inode_offset;
	super_d.map_data_blks       = super.map_data_blks;
	super_d.map_data_offset     = super.map_data_offset;
	super_d.inode_offset        = super.inode_offset;
    super_d.data_offset         = super.data_offset;
	super_d.cur_inode_num       = super.cur_inode_num;
// 写回super块
    if (guafs_driver_write(0, (uint8_t *)&super_d, 
                     sizeof(struct guafs_super_d)) != 0) {
		perror("error in destroy writing the super_d");
        fuse_exit(fuse_get_context()->fuse);
    }
//写回inode_map
	if (guafs_driver_write(super_d.map_inode_offset, (uint8_t *)(super.inode_map), 
							BLKS_SZ(super_d.map_inode_blks)) != 0) {
			perror("error in destroy writing the inode_map");
			fuse_exit(fuse_get_context()->fuse);
		}
//写回data_map

	if (guafs_driver_write(super_d.map_data_offset, (uint8_t *)(super.data_map), 
							BLKS_SZ(super_d.map_data_blks)) != 0) {
			perror("error in destroy writing the data_map");
			fuse_exit(fuse_get_context()->fuse);
		}

// 使用malloc申请的内存需要手动释放
    free(super.inode_map);
	free(super.data_map);
	ddriver_close(super.fd);

	return ;
}

/**
 * @brief 创建目录
 * 
 * @param path 相对于挂载点的路径
 * @param mode 创建模式（只读？只写？），可忽略
 * @return int 0成功，否则失败
 */
int guafs_mkdir(const char* path, mode_t mode) {
	(void)mode;
	int is_find, is_root;
	char* fname;
	struct guafs_dentry* last_dentry = guafs_lookup(path, &is_find, &is_root);
	struct guafs_dentry* dentry;
	struct guafs_inode*  inode;

	if (is_find) {
		return -EEXIST;
	}

	if (last_dentry->inode==REG_FILE) {
		return -ENXIO;
	}

	fname  = guafs_get_fname(path);
	dentry = new_dentry(fname, DIR); 
	dentry->parent = last_dentry;
	inode  = guafs_alloc_inode(dentry);
	if(!inode)
	{
		return -EACCES;
	}
	guafs_alloc_dentry(last_dentry->inode, dentry);
	
	return 0;
}

/**
 * @brief 获取文件或目录的属性，该函数非常重要
 * 
 * @param path 相对于挂载点的路径
 * @param guafs_stat 返回状态
 * @return int 0成功，否则失败
 */
int guafs_getattr(const char* path, struct stat * guafs_stat) {
	/* TODO: 解析路径，获取Inode，填充guafs_stat，可参考/fs/simplefs/sfs.c的sfs_getattr()函数实现 */
	int	is_find, is_root;
	struct guafs_dentry* dentry = guafs_lookup(path, &is_find, &is_root);
	if (is_find == 0) {
		return -ENOENT;
	}

	if (dentry->inode->ftype==DIR) {
		// stat是fuse提供的变量，按照他的赋值就好
		guafs_stat->st_mode = S_IFDIR | 0777;
		guafs_stat->st_size = dentry->inode->dir_cnt * sizeof(struct guafs_dentry_d);
	}
	else if (dentry->inode->ftype==REG_FILE) {
		guafs_stat->st_mode = S_IFREG | 0777;
		guafs_stat->st_size = dentry->inode->size;
	}

	guafs_stat->st_nlink = 1;
	guafs_stat->st_uid 	 = getuid();
	guafs_stat->st_gid 	 = getgid();
	guafs_stat->st_atime   = time(NULL);
	guafs_stat->st_mtime   = time(NULL);
	guafs_stat->st_blksize = IO_SIZE;

	if (is_root) {
		guafs_stat->st_size	= super.usage_sz; 
		guafs_stat->st_blocks = super.disk_sz / IO_SIZE;
		guafs_stat->st_nlink  = 2;		/* !特殊，根目录link数为2 */
	}
	return 0;
}

/**
 * @brief 遍历目录项，填充至buf，并交给FUSE输出
 * 
 * @param path 相对于挂载点的路径
 * @param buf 输出buffer
 * @param filler 参数讲解:
 * 
 * typedef int (*fuse_fill_dir_t) (void *buf, const char *name,
 *				const struct stat *stbuf, off_t off)
 * buf: name会被复制到buf中
 * name: dentry名字
 * stbuf: 文件状态，可忽略
 * off: 下一次offset从哪里开始，这里可以理解为第几个dentry
 * 
 * @param offset 第几个目录项？
 * @param fi 可忽略
 * @return int 0成功，否则失败
 */
int guafs_readdir(const char * path, void * buf, fuse_fill_dir_t filler, off_t offset,
			    		 struct fuse_file_info * fi) {
    /* TODO: 解析路径，获取目录的Inode，并读取目录项，利用filler填充到buf，可参考/fs/simplefs/sfs.c的sfs_readdir()函数实现 */
	int	is_find, is_root;
	int		cur_dir = offset;

	struct guafs_dentry* dentry = guafs_lookup(path, &is_find, &is_root);
	struct guafs_dentry* sub_dentry;
	struct guafs_inode* inode;
	if (is_find) {
		inode = dentry->inode;
		sub_dentry = guafs_get_dentry(inode, cur_dir);
		if (sub_dentry) {
			filler(buf, sub_dentry->fname, NULL, ++offset);
		}
		return 0;
	}
	return -ENOENT;
}

/**
 * @brief 创建文件
 * 
 * @param path 相对于挂载点的路径
 * @param mode 创建文件的模式，可忽略
 * @param dev 设备类型，可忽略
 * @return int 0成功，否则失败
 */
int guafs_mknod(const char* path, mode_t mode, dev_t dev) {
	/* TODO: 解析路径，并创建相应的文件 */
	int	is_find, is_root;
	
	struct guafs_dentry* last_dentry = guafs_lookup(path, &is_find, &is_root);
	struct guafs_dentry* dentry;
	struct guafs_inode* inode;
	char* fname;
	
	// 要创建的文件已经存在了
	if (is_find == 1) {
		return -EEXIST;
	}
	// 从路径裁剪出文件名
	fname = guafs_get_fname(path);
	
	// 根据文件类型创建dentry项
	if (S_ISREG(mode)) {
		dentry = new_dentry(fname, REG_FILE);
	}
	else if (S_ISDIR(mode)) {
		dentry = new_dentry(fname, DIR);
	}


	dentry->parent = last_dentry;
	inode = guafs_alloc_inode(dentry);
	if(!inode)
	{
		return -EACCES;
	}
	guafs_alloc_dentry(last_dentry->inode, dentry);

	return 0;
}

/**
 * @brief 修改时间，为了不让touch报错 
 * 
 * @param path 相对于挂载点的路径
 * @param tv 实践
 * @return int 0成功，否则失败
 */
int guafs_utimens(const char* path, const struct timespec tv[2]) {
	(void)path;
	return 0;
}


/******************************************************************************
* SECTION: 选做函数实现
*******************************************************************************/
/**
 * @brief 写入文件
 * 
 * @param path 相对于挂载点的路径
 * @param buf 写入的内容
 * @param size 写入的字节数
 * @param offset 相对文件的偏移
 * @param fi 可忽略
 * @return int 写入大小
 */
int guafs_write(const char* path, const char* buf, size_t size, off_t offset,
		        struct fuse_file_info* fi) {
	/* 选做 */
	return size;
}

/**
 * @brief 读取文件
 * 
 * @param path 相对于挂载点的路径
 * @param buf 读取的内容
 * @param size 读取的字节数
 * @param offset 相对文件的偏移
 * @param fi 可忽略
 * @return int 读取大小
 */
int guafs_read(const char* path, char* buf, size_t size, off_t offset,
		       struct fuse_file_info* fi) {
	/* 选做 */
	return size;			   
}

/**
 * @brief 删除文件
 * 
 * @param path 相对于挂载点的路径
 * @return int 0成功，否则失败
 */
int guafs_unlink(const char* path) {
	/* 选做 */
	return 0;
}

/**
 * @brief 删除目录
 * 
 * 一个可能的删除目录操作如下：
 * rm ./tests/mnt/j/ -r
 *  1) Step 1. rm ./tests/mnt/j/j
 *  2) Step 2. rm ./tests/mnt/j
 * 即，先删除最深层的文件，再删除目录文件本身
 * 
 * @param path 相对于挂载点的路径
 * @return int 0成功，否则失败
 */
int guafs_rmdir(const char* path) {
	/* 选做 */
	return 0;
}

/**
 * @brief 重命名文件 
 * 
 * @param from 源文件路径
 * @param to 目标文件路径
 * @return int 0成功，否则失败
 */
int guafs_rename(const char* from, const char* to) {
	/* 选做 */
	return 0;
}

/**
 * @brief 打开文件，可以在这里维护fi的信息，例如，fi->fh可以理解为一个64位指针，可以把自己想保存的数据结构
 * 保存在fh中
 * 
 * @param path 相对于挂载点的路径
 * @param fi 文件信息
 * @return int 0成功，否则失败
 */
int guafs_open(const char* path, struct fuse_file_info* fi) {
	/* 选做 */
	return 0;
}

/**
 * @brief 打开目录文件
 * 
 * @param path 相对于挂载点的路径
 * @param fi 文件信息
 * @return int 0成功，否则失败
 */
int guafs_opendir(const char* path, struct fuse_file_info* fi) {
	/* 选做 */
	return 0;
}

/**
 * @brief 改变文件大小
 * 
 * @param path 相对于挂载点的路径
 * @param offset 改变后文件大小
 * @return int 0成功，否则失败
 */
int guafs_truncate(const char* path, off_t offset) {
	/* 选做 */
	return 0;
}


/**
 * @brief 访问文件，因为读写文件时需要查看权限
 * 
 * @param path 相对于挂载点的路径
 * @param type 访问类别
 * R_OK: Test for read permission. 
 * W_OK: Test for write permission.
 * X_OK: Test for execute permission.
 * F_OK: Test for existence. 
 * 
 * @return int 0成功，否则失败
 */
int guafs_access(const char* path, int type) {
	/* 选做: 解析路径，判断是否存在 */
	return 0;
}	
/******************************************************************************
* SECTION: FUSE入口
*******************************************************************************/
int main(int argc, char **argv)
{
    int ret;
	struct fuse_args args = FUSE_ARGS_INIT(argc, argv);

	guafs_options.device = strdup("/home/guests/190110401/ddriver");

	if (fuse_opt_parse(&args, &guafs_options, option_spec, NULL) == -1)
		return -1;
	
	ret = fuse_main(args.argc, args.argv, &operations, NULL);
	fuse_opt_free_args(&args);
	return ret;
}